/**
 *    Copyright (C) 2009 Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#ifndef _CUBEFS_CORE_API_REFLECTION_TYPE_HPP
#define _CUBEFS_CORE_API_REFLECTION_TYPE_HPP

#include <cubefs/api/reflection/type.hpp>
#include <cubefs/api/exceptions.hpp>

#include <cubefs/core/api/reflection/typed.hpp>

#include <vector>

#define CUBEFS_SOURCE "cubefs/core/api/reflection/type.hpp:" CUBEFS_AT

namespace cubefs { namespace api {

template <typename Pattern, typename Functor = Pattern> class BasicOperation;
template <typename Pattern, typename Functor = Pattern> class BasicStaticOperation;

template <typename Interface>
class TypeImplT: public TypedImplT<Interface> {
	typedef TypeImplT<Interface> 	Me;
	typedef TypedImplT<Interface> 	Base;

public:

	TypeImplT(StringRef name, Type* parent):
		name_(name), parent_(parent)
		{
			GetCoreFactory()->GetCoreTypeSystem()->Register(this);
		}

	 TypeImplT(StringRef name, Type* parent, const ListOfOperations &operations):
	        name_(name), parent_(parent), operations_(operations)
	    {
	        GetCoreFactory()->GetCoreTypeSystem()->Register(this);
	    }

	virtual ~TypeImplT() throw () {
		for (Int c = 0; c < operations_.size(); c++) {
			delete operations_[c];
		}
	}

	virtual StringRef name() const {
		return name_;
	}

	virtual Operation* GetOperation(Int index) const {
		Type* prnt = parent();

		Int base = prnt != NULL ? prnt->GetOperationsCount() : 0;

		if (index < base) {
			return prnt->GetOperation(index);
		}
		else {
			index -= base;
			if (index >= 0 && index < operations_.size()) {
				return operations_[index];
			}
			else {
				throw BoundsException(CUBEFS_SOURCE, index, 0, operations_.size());
			}
		}
	}

	virtual Operation* FindOperation(StringRef name, TypedList<Type*>* arguments) const {
		for (Int c = 0; c < operations_.size(); c++)
		{
			Operation* op = operations_[c];
			if (op->name() == name)
			{
				Int args = op->GetArgumentsCount();
				Int argumentsSize = arguments != NULL ? arguments->size() : 0;
				if (argumentsSize == 0 && args == 0)
				{
					return op;
				}
				else if (args == argumentsSize)
				{
					bool match = true;
					for (Int d = 0; d < op->GetArgumentsCount();d++) {
						const Argument* arg = op->GetArgument(d);

						if (!arguments->Get(d)->IsSameOrSubclass(arg->GetParameterType()))
						{
							match = false;
							break;
						}
					}

					if (match)
					{
						return op;
					}
				}
			}
		}

		if (parent() != NULL)
		{
			return parent()->FindOperation(name, arguments);
		}
		else {
			return NULL;
		}
	}


	virtual Type* parent() const {
		return parent_;
	}

	virtual Int GetOperationsCount() const {
		Type* prnt = parent();
		Int base = prnt != NULL ? prnt->GetOperationsCount() : 0;
		return base + operations_.size();
	}

	Operation* AddOperation(Operation *operation) {
		operations_.push_back(operation);
	}

	void AddOperations(const ListOfOperations &operations) {
		operations_.insert(operations_.end(), operations.begin(), operations.end());
	}

	virtual bool IsSameOrSubclass(const Type* type) const {
		if (this == type) {
			return true;
		}
		else {
			Type* prnt = parent();
			if (prnt != NULL) {
				return prnt->IsSameOrSubclass(type);
			}
			else {
				return false;
			}
		}
	}

	static void InitType() {
		type_ = new Me(TypeNameFactory<Interface>::name(), Typed::MyType());
	}

	static void InitOperations();

	CUBEFS_DECLARE_REFLECTION;

private:
	Type*                               parent_;
	std::vector<Operation*>         	operations_;
	String                         		name_;
};

template <typename Interface>
Type* TypeImplT<Interface>::type_ 					= NULL;

typedef TypeImplT<Type> 							TypeImpl;





template <typename CPPType>
struct TypeFactory {
    static Type* type() {
        return CPPType::MyType();
    }
};



template <>
struct TypeFactory<void> {
    static Type* type() {
        return VoidValue::MyType();
    }
};

template <typename CPPType>
struct TypeFactory<CPPType*> {
    static Type* type() {
        return CPPType::MyType();
    }
};


}}

#undef CUBEFS_SOURCE
#endif
